
#ifndef  NDEBUG
#define NDEBUG
#endif 

#include <assert.h>
#include "simple_protocol.h"
#include "utility.h"
#include "debug.h"
#include <log.h>
#include "log/log2.h"

#include "BSP_Sleep.h"

#include "task_config.h"
#include "FreeRTOS.h"
#include "stream_buffer.h"
#include "queue.h"
#include "event_groups.h"

#include "tag_type.h"

#include <uart/uart.h>
#include <usb/usb.h>
#include <Reader/Reader.h>
#include <Reader/ReaderBLE.h>

#include <Modules/BLE/BLE_IF.h>
#include <Modules/BLE/BLE.h>
#include <Modules/config/config3.h>

#define PROTOCOL_BUF_RESERVE			16

#define PROTOCOL_CMD_BUF_SIZE		2048
#define PROTOCOL_RESP_BUF_SIZE		1024

typedef struct {
	uint8_t state;
	TaskHandle_t hAsynTask;
	QueueHandle_t hCmdQ;
	QueueHandle_t hRespQ;
}protocol_async_context_t;

static protocol_async_context_t protocol_async_ctx;
static EventGroupHandle_t protocol_event;
typedef struct {
	int result;
	api_class_t api;
	buffer_t command;
	buffer_t response;
	uint8_t command_buffer[PROTOCOL_CMD_BUF_SIZE];
	uint8_t response_buffer[PROTOCOL_RESP_BUF_SIZE];
}protocol_work_t;

static protocol_work_t protocol_work;
simple_protocol_context_t protocol_context;
static uint8_t command_buffer[PROTOCOL_CMD_BUF_SIZE];
static uint8_t response_buffer[PROTOCOL_RESP_BUF_SIZE];

simpleConfig_t simpleConfig;
/*--------------------------------------------------------------------------------*/


static void async_task(void *arg){
	protocol_async_context_t* actx = (protocol_async_context_t* )arg;
	protocol_work_t* work;

	while(1){
		if(xQueueReceive(actx->hCmdQ, &work, portMAX_DELAY) == pdTRUE){
			actx->state = 1; 	//忙
			work->result = work->api.entry(&work->command, &work->response);
			if(xQueueSend(actx->hRespQ, &work, 10) != pdPASS){
				TRACE_E("resp fail!");
			}
			actx->state = 0;
		}
	}
}

static void protocol_async_context_init(protocol_async_context_t* actx){
	actx->hCmdQ = xQueueCreate(1, sizeof(void*));
	actx->hRespQ = xQueueCreate(1, sizeof(void*));
	actx->state = 0;		//idle
	if(xTaskCreate(async_task, "async", 1024, actx, ASYNC_TASK_PRIORITY, &actx->hAsynTask) != pdPASS){
		TRACE_E("create async task fail!");
	}
}

static void protocol_work_init(protocol_work_t* work){
	net_buf_simple_init_with_data(&work->command, work->command_buffer, sizeof(work->command_buffer));
	net_buf_simple_init_with_data(&work->response, work->response_buffer, sizeof(work->response_buffer));
}

static void protocol_work_setup(protocol_work_t* work, const api_class_t* api, buffer_t* cmd){
	work->api = *api;
	net_buf_simple_reset(&work->command);
	net_buf_simple_add_mem(&work->command, cmd->data, cmd->len);
	net_buf_simple_init(&work->response, PROTOCOL_BUF_RESERVE);
	work->result = -1;
}

// static int protocol_execute_async(simple_protocol_context_t* ctx, protocol_async_context_t* actx, const api_class_t* api, int wtime){
// 	//清除应答队列
// 	xQueueReset(actx->hRespQ);
// 	//当前是否忙
// 	if(actx->state){
// 		vTaskDelay(100);
// 		return ERR_BUSY;
// 	}
// 	//加入执行队列
// 	protocol_work_t *work = &protocol_work;
// 	protocol_work_setup(work, api, &ctx->command);
// 	if(xQueueSend(actx->hCmdQ, &work, 10) != pdPASS){
// 		TRACE_E("send fail!");
// 		return ERR_BUSY;
// 	}
// 	//等待执行结果
// 	work = NULL;
// 	if(xQueueReceive(actx->hRespQ, &work, wtime) != pdPASS){
// 		TRACE_E("receive timeout!");
// 		return ERR_BUSY;
// 	}
// 	//返回执行结果
// 	if(work->response.len > 0){
// 		net_buf_simple_add_mem(&ctx->response, work->response.data, work->response.len);
// 	}

// 	return work->result;
// }
static int protocol_execute_async(simple_protocol_context_t* ctx, protocol_async_context_t* actx, const api_class_t* api, int wtime){
	//清除应答队列
	xQueueReset(actx->hRespQ);
	//当前是否忙
	if(reader.state & RD_ST_CONFIG){
		vTaskDelay(100);
		return ERR_BUSY;
	}
	//加入执行队列
	protocol_work_t *work = &protocol_work;
	protocol_work_setup(work, api, &ctx->command);
	if(xQueueSend(actx->hCmdQ, &work, 1000) != pdPASS){
		TRACE_E("send fail!");
		net_buf_simple_add_u8(&ctx->response, 0);
		return ERR_NONE;
	}
	//等待执行结果
	work = NULL;
	if(xQueueReceive(actx->hRespQ, &work, wtime) != pdPASS){
		TRACE_E("receive timeout!");
		net_buf_simple_add_u8(&ctx->response, 0);
		return ERR_NONE;
	}
	//返回执行结果
	if(work->response.len > 0){
		net_buf_simple_add_mem(&ctx->response, work->response.data, work->response.len);
	}

	return work->result;
}


// static int protocol_is_async_api(const api_class_t* api, uint8_t fun){
// 	static const uint8_t async_codes[] = {
// 		API_CLASS_RF,
// 		API_CLASS_MIFARE_CLASSIC,
// 		API_CLASS_MIFARE_ULTRALIGHT,
// 		API_CLASS_DESFIRE,
// 		API_CLASS_ISO7816,
// 		API_CLASS_ICLASS,
// 		API_CLASS_ISO14443,
// 	};
		
// 	if(api->code == API_CLASS_ICLASS){
// 		//rdy 定义指令
// 		if(0x80 <= fun && fun <= 0x8F){
// 			return 0;
// 		}
		
// 		return 1;
// 	}
	
// 	for(int i = 0; i < sizeof(async_codes); i++){
// 		if(api->code == async_codes[i]){
// 			return 1;
// 		}
// 	}
	
// 	return 0;
// }
static int protocol_is_async_api(uint8_t code, uint8_t func){
	if(code == API_CLASS_RF && (func == 0 || func == 1)){
		return 1;
	}
	
	return 0;
}
#if 0
static int protocol_execute(simple_protocol_context_t* ctx, const api_class_t* api){
	if(protocol_is_async_api(api)){
		return protocol_execute_async(ctx, &protocol_async_ctx, api, 1000);
	}
	return api->entry(&ctx->command, &ctx->response);
}
#endif 

/*--------------------------------------------------------------------------------*/
SIMPLE_API_START_DEFINE;
SIMPLE_API_END_DEFINE;

static void protocol_debug_buf(const char* str, const buffer_t* buf){
	LOG_B(str, buf->data, buf->len, 16);
}

static int protocol_verify_command(const buffer_t* command){
	if(command->len < 2){
		LOG_E("command too short!");
		return ERR_PARSER;
	}
	
	return ERR_NONE;
}

void protocol_response_result(buffer_t* response, int result){
	uint8_t status = result ? 0 : 1;
	net_buf_simple_push_u8(response, status);
}

int protocol_call_api(const api_entry_t* api_list, uint8_t api_num, 
							buffer_t* command, buffer_t* response){
	
	uint8_t fun = net_buf_simple_pull_u8(command);
	
	if(fun >= api_num){
		return ERR_UNKNOWN_FUNCTION;
	}
	
	if(api_list[fun]){
		return api_list[fun](command, response);
	}
	
	return ERR_INVALID_FUNCTION;
}
							
int protocol_call_fun(uint8_t fun_code, const api_func_t fun_list[], uint8_t fun_num, buffer_t* command, buffer_t* response){
	for(int i = 0; i < fun_num; i++){
		if(fun_list[i].func == fun_code){
			return fun_list[i].entry(command, response);
		}
	}
	return ERR_INVALID_FUNCTION;
}

static int protocol_exec_command(simple_protocol_context_t* ctx){
	protocol_debug_buf("cmd: ", &ctx->command);
	const api_class_t* api = SIMPLE_API_START;
	const api_class_t* end = SIMPLE_API_END;
	uint8_t code = net_buf_simple_pull_u8(&ctx->command);
	uint8_t func = ctx->command.data[0];
	
	if(code == API_CLASS_ICLASS && !(reader.attr & RD_ATTR_SE)){
		LOG2_ERR("API_CLASS_ICLASS not support!");
		return ERR_UNKNOWN_FUNCTION;
	}
	
	if(code == API_CLASS_BLE && !(reader.attr & RD_ATTR_BT)){
		LOG2_ERR("API_CLASS_BLE not support!");
		return ERR_UNKNOWN_FUNCTION;
	}
	
	api++;
	while(api < end){
		if(api->code == code){
			if(protocol_is_async_api(api->code, func)){
				return protocol_execute_async(ctx, &protocol_async_ctx, api, 3000);
			}
			return api->entry(&ctx->command, &ctx->response);
		}
		api++;
	}
	LOG2_ERR("unknown api code %02X", code);
	return ERR_UNKNOWN_FUNCTION;
}

static int protocol_process_command(simple_protocol_context_t* ctx){
	net_buf_simple_init(&ctx->response, PROTOCOL_BUF_RESERVE);
	int ret = protocol_verify_command(&ctx->command);
	if(ret){
		return ret;
	}
	
	return protocol_exec_command(ctx);
}

/*--------------------------------------------------------------------------------*/
static uint16_t update_crc(uint16_t crc,uint8_t byte);

static uint16_t protocol_calc_crc(uint16_t crc, const uint8_t* buffer, uint32_t length){
	
	for(uint32_t i = 0; i < length; i++){
		crc = update_crc(crc, buffer[i]);
	}
	
	return crc;
}

#if 0
static void protocol_command_buffer_add(simple_protocol_context_t* ctx, uint8_t byte){
	if(ctx->cmd_len < sizeof(command_buffer)){
		ctx->cmd_buf[ctx->cmd_len++] = byte;
	}
}

static void protocol_command_net_buf_simple_init(simple_protocol_context_t* ctx){
	ctx->cmd_len = 0;
}

static void protocol_response_net_buf_simple_init(simple_protocol_context_t* ctx){
	ctx->resp_len = 0;
}
#endif 

/*----------------------------------------------------------------------*/

static uint8_t protocol_str_to_hex(uint8_t ch){
	if('0' <= ch && ch <= '9'){
		return ch - '0';
	}
	if('A' <= ch && ch <= 'F'){
		return ch - ('A' - 0x0A);
	}
	if('a' <= ch && ch <= 'f'){
		return ch - ('a' - 0x0A);
	}
	return 0xFF;
}

static void protocol_clear_rx(uint8_t channel){
	
	if(channel == SIMPLE_PROTOCOL_COM_USB){
		usb_cdc_clear_rx();
	}
	else{
		uart_clear_rx();
	}
}

static int protocol_get_byte_binary(uint8_t channel, int wtime){
	if(channel == SIMPLE_PROTOCOL_COM_USB){
		return usb_cdc_get_byte(wtime);
	}
	else{
		return uart_get_byte(wtime);
	}
}

static int protocol_get_byte_ascii(uint8_t channel, int wtime){
	int hi,lo;
	hi = protocol_get_byte_binary(channel, wtime);
	if(hi < 0){
		return hi;
	}

	if(hi == '\r'){
		return 0x100;
	}
	
	lo = protocol_get_byte_binary(channel, wtime + 10);
	if(lo < 0){
		return lo;
	}
	
	uint8_t h = protocol_str_to_hex(hi);
	uint8_t l = protocol_str_to_hex(lo);
	if(h > 0x0F || l > 0x0F){
		LOG_E("rx hi=%02X, lo=%02X", hi, lo);
		return -2;
	}
	
	return h << 4 | l;
}

#if 0
static int protocol_get_byte(uint8_t mode, uint8_t channel, int wtime){
	if(mode == PROTOCOL_MODE_ASCII){
		return protocol_get_byte_ascii(channel, wtime);
	}
	else{
		return protocol_get_byte_binary(channel, wtime);
	}
}
#endif 

static int protocol_receive_command_ascii(buffer_t* command, uint8_t channel, int wtime){
	int byte = protocol_get_byte_ascii(channel, wtime);
	if(byte < 0){
		return byte;
	}
	if(byte > 0xFF){
		LOG_D("end");
		return 0;
	}
	
	net_buf_simple_init(command, PROTOCOL_BUF_RESERVE);
	net_buf_simple_add_u8(command, byte);
	int len = net_buf_simple_tailroom(command);
	while(1){
		byte = protocol_get_byte_ascii(channel, 100);
		if(byte < 0){
			LOG_E("receive fail:%d", byte);
			return byte;
		}
		
		if(byte > 0xFF){
			LOG_D("end");
			break;
		}
		
		if(len > 0){
			len--;
			net_buf_simple_add_u8(command, byte);
		}
	}
	
	return 0;
}


static int protocol_recieve_command_length(uint8_t channel, int wtime){
	int lo, hi;
	
	lo = protocol_get_byte_binary(channel, wtime);
	if(lo < 0){
		return lo;
	}
	
	hi = protocol_get_byte_binary(channel, 100);
	if(hi < 0){
		return hi;
	}

	return hi << 8 | lo;
}

static int protocol_receive_command_binary(buffer_t* command, uint8_t channel, int wtime){
	int len = protocol_recieve_command_length(channel, wtime);
	if(len < 0){
		return len;
	}
	
	net_buf_simple_init(command, PROTOCOL_BUF_RESERVE);
	int rlen = net_buf_simple_tailroom(command);
	while(len > 0){
		int byte = protocol_get_byte_binary(channel, 100);
		if(byte < 0){
			LOG_E("receive fail: %d", byte);
			return byte;
		}
		if(rlen > 0){
			rlen--;
			net_buf_simple_add_u8(command, byte);
		}
		len--;
	}

	return 0;
}

static int protocol_check_command(buffer_t* command){
	if(command->len < 3){
		LOG_E("invalid len: %d", command->len);
		return 0;
	}
	uint16_t crc0 = net_buf_simple_remove_le16(command);	
	uint16_t crc = protocol_calc_crc(0xFFFF, command->data, command->len);
	if(crc != crc0){
		LOG_E("crc error: rx=0x%04X, calc=0x%04X", crc0, crc);
		return 0;
	}
	
	return 1;
}

static int protocol_receive_command(simple_protocol_context_t* ctx, uint8_t channel, int wtime){
	int ret;
	buffer_t* cmd = &ctx->command;
	
	if((ctx->flag & PROTOCOL_MODE_MASK) == PROTOCOL_MODE_ASCII){
		ret = protocol_receive_command_ascii(cmd, channel, wtime);
	}
	else{
		ret = protocol_receive_command_binary(cmd, channel, wtime);
	}

	if(ret){
		return ret;
	}
	
	if((ctx->flag & PROTOCOL_CRC_MASK) == PROTOCOL_CRC_ENABLE){
		if(!protocol_check_command(cmd)){
			return -3;
		}
	}
	
	return 0;
}

/*----------------------------------------------------------------------*/

static uint8_t protocol_hex_to_str(uint8_t hex){
	if(hex < 0x0A){
		return '0' + hex;
	}
	
	if(hex < 0x10){
		return ('A' - 0x0A) + hex;
	}
	
	return 0xFF;
}

static uint8_t protocol_send_cach[64];
static uint8_t protocol_send_cach_len;

static inline int protocol_cach_space(void){
	return sizeof(protocol_send_cach) - protocol_send_cach_len;
}

static void protocol_cach_flush(uint8_t channel){
	if(protocol_send_cach_len == 0){
		return ;
	}
	
	if(channel == SIMPLE_PROTOCOL_COM_USB){
		usb_cdc_data_send(protocol_send_cach, protocol_send_cach_len);
	}
	else{
		uart_send(protocol_send_cach, protocol_send_cach_len);
	}

	protocol_send_cach_len = 0;
}

static int protocol_cach_write(const uint8_t* buffer, uint16_t length){
	int space = protocol_cach_space();
	int len = length < space ? length : space;
	
	if(len){
		memcpy(protocol_send_cach + protocol_send_cach_len, buffer, len);
		protocol_send_cach_len += len;
	}

	return len;
}

static void protocol_send_binary(uint8_t channel, const uint8_t* buffer, uint16_t length){
	uint16_t remain = length;
	const uint8_t* data = buffer;
	
	while(remain > 0){
		int len = protocol_cach_write(data, remain);
		remain -= len;
		data += len;
		int space = protocol_cach_space();
		if(space < 1){
			protocol_cach_flush(channel);
		}
	}
}

static void protocol_send_ascii(uint8_t channel, const uint8_t* buffer, uint16_t length){
	uint32_t i;
	uint8_t buf[2];
	
	for(i = 0; i < length; i++){
		buf[0] = protocol_hex_to_str((buffer[i] >> 4) & 0x0F);
		buf[1] = protocol_hex_to_str(buffer[i] & 0x0F);
		protocol_send_binary(channel, buf, sizeof(buf));
	}
}

static void protocol_response_ascii(simple_protocol_context_t* ctx, uint8_t channel){
	buffer_t* resp = &ctx->response;
	protocol_send_ascii(channel, resp->data, resp->len);
	protocol_send_binary(channel, (const uint8_t*)"\r", 1);
}

static void protocol_response_binary(simple_protocol_context_t* ctx, uint8_t channel){
	buffer_t* resp = &ctx->response;
	uint16_t len = resp->len;
	protocol_send_binary(channel, (uint8_t*)&len, sizeof(len));
	protocol_send_binary(channel, resp->data, resp->len);
}

static void protocol_response(simple_protocol_context_t* ctx, uint32_t flag, uint8_t channel, uint8_t err){
	net_buf_simple_push_u8(&ctx->response, err);
	
	if(flag & PROTOCOL_CRC_ENABLE){
		uint16_t crc;
		crc = protocol_calc_crc(0xFFFF, ctx->response.data, ctx->response.len);
		net_buf_simple_add_le16(&ctx->response, crc);
	}
	protocol_debug_buf("resp: ", &ctx->response);
	if((flag & PROTOCOL_MODE_MASK) == PROTOCOL_MODE_ASCII){
		protocol_response_ascii(ctx, channel);
	}
	else{
		protocol_response_binary(ctx, channel);
	}
	protocol_cach_flush(channel);
}

/*----------------------------------------------------------------------*/

static void protocol_process(simple_protocol_context_t* ctx, uint8_t channel, int wtime){
	if(protocol_receive_command(ctx, channel, wtime) == 0){		
		uint32_t flag = ctx->flag;
		int ret = protocol_process_command(ctx);
		protocol_response(ctx, flag, channel, ret);
	}
	else{
		//指令解析失败，清除缓存
		protocol_clear_rx(channel);
	}
}

#if 0

static int BLE_Check(void){
	int i;
	int ret = 0;
	uint8_t state = 0;
	
	for(i = 0; i < 3; i++){
		if(!BLE_GetState(&state)){
			ret = 1;
			break;
		}
		vTaskDelay(50);
	}
	
	return ret;
}

//设置MCU版本信息
void GetMCUInfo(MCU_Info_T* info);

static int MCU_Info_Cmp(const MCU_Info_T* info1, const MCU_Info_T* info2){
	if(strcmp((char*)info1->ver, (char*)info2->ver)){
		return 1;
	}

	if(strcmp((char*)info1->Model, (char*)info2->Model)){
		return 1;
	}
	
	return 0;
}

static void BLE_Setup(void){
	MCU_Info_T info1, info2;
	int i;

	for(i = 0; i < 3; i++){
		if(!BLE_GetMCUInfo(&info2)){
			break;
		}
		vTaskDelay(50);
	}

	if(i == 3){
		return ;
	}
	
	GetMCUInfo(&info1);
	if(!MCU_Info_Cmp(&info1, &info2)){
		return ;
	}
	
	for(i = 0; i < 3; i++){
		if(!BLE_SetMCUInfo(&info1)){
			break;
		}
		vTaskDelay(50);
	}
}
#endif 

/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
static void simpleProtocol_init(void)
{
	protocol_send_cach_len = 0;
	protocol_context.flag = PROTOCOL_CRC_DISABLE | PROTOCOL_MODE_ASCII;
	net_buf_simple_init_with_data(&protocol_context.command, command_buffer, sizeof(command_buffer));
	net_buf_simple_init_with_data(&protocol_context.response, response_buffer, sizeof(response_buffer));
	protocol_work_init(&protocol_work);
	protocol_async_context_init(&protocol_async_ctx);
	protocol_event = xEventGroupCreate();
	
	if(reader.attr & RD_ATTR_LF){
		reader.lfFlags = LFTAG_DEFAULT;
	}
	else{
		reader.lfFlags = 0;
	}
	
	if(reader.attr & RD_ATTR_HF){
		reader.hfFlags = HFTAG_DEFAULT;
	}
	else{
		reader.hfFlags = 0;
	}

	if(!(reader.attr & RD_ATTR_HIDPROX)){
		reader.lfFlags &= ~ TAG_MASK_HIDPROX;
	}
	
	if(!(reader.attr & RD_ATTR_HIDICLASS)){
		reader.hfFlags &= ~ TAG_MASK_HIDICLASS;
	}
	else{
		reader.hfFlags |= TAG_MASK_APPLEC;
	}
	
#if 1
	BLE_IF_Init();
	Reader_BLE_Init();
#endif 

//		reader.config = RD_CFG_HFFirst | RD_CFG_MFCheck;
	reader.config = RD_CFG_HFFirst;
	Reader_NFC_LoadProfile(reader.hfFlags, 0, 0, 0);
	usb_cdc_init();
	uart_init();
}

void simpleProtocol_Notify(uint8_t src){
   /* Notify the task that the transmission is complete by setting the TX_BIT
   in the task's notification value. */
   if(protocol_event){
	   BaseType_t xHigherPriorityTaskWoken = pdFALSE;
	   xEventGroupSetBitsFromISR(protocol_event, src, &xHigherPriorityTaskWoken);
	   portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
   }
}

static int simpleProtocol_CheckRx(uint32_t* notifiedValue, TickType_t xTicksToWait){
	EventBits_t Bits = xEventGroupWaitBits(protocol_event, PROTOCOL_UART_BIT|PROTOCOL_USB_BIT, pdTRUE, pdFALSE, xTicksToWait);	
	if((Bits & PROTOCOL_UART_BIT) && uart_check_rx()){
		*notifiedValue = PROTOCOL_UART_BIT;
		return 1;
	}
	
	if((Bits & PROTOCOL_USB_BIT) && usb_cdc_check_rx()){
		*notifiedValue = PROTOCOL_USB_BIT;
		return 1;
	}
	
	return 0;
}

void simpleProtocol_task(void *arg)
{
	int ret;
	uint32_t notifiedValue;
	const TickType_t xTicksToWait = 100;

	simpleProtocol_init();
	
	while(1)
	{
		ret = simpleProtocol_CheckRx(&notifiedValue, xTicksToWait);
		if( ret )
		{
			if( ( notifiedValue & PROTOCOL_UART_BIT ) != 0 )
			{
				protocol_process(&protocol_context, SIMPLE_PROTOCOL_COM_UART, 1);
			}

			if( ( notifiedValue & PROTOCOL_USB_BIT ) != 0 )
			{
				protocol_process(&protocol_context, SIMPLE_PROTOCOL_COM_USB, 1);
			}
		}
		config3_sync();
	}
}



static uint16_t update_crc(uint16_t crc,uint8_t byte)
{
	// Update CCITT CRC (reverse polynom 0x8408)
	byte ^= (uint8_t)crc;
	byte ^= (uint8_t)(byte << 4);
	return (uint16_t)(((byte << 8) | (crc >> 8)) ^ (byte >> 4) ^ (byte << 3));
}



